//===--- PILModule.cpp - PILModule implementation -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "pil-module"

#include "polarphp/pil/lang/PILModule.h"
#include "polarphp/pil/lang/internal/Linker.h"
#include "polarphp/ast/GenericEnvironment.h"
#include "polarphp/ast/InterfaceConformance.h"
#include "polarphp/clangimporter/ClangModule.h"
#include "polarphp/pil/lang/FormalLinkage.h"
#include "polarphp/pil/lang/Notifications.h"
#include "polarphp/pil/lang/PILDebugScope.h"
#include "polarphp/pil/lang/PILValue.h"
#include "polarphp/pil/lang/PILVisitor.h"
#include "polarphp/serialization/SerializedPILLoader.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/YAMLTraits.h"
#include <functional>

using namespace polar;
using namespace polar::lowering;

class PILModule::SerializationCallback final
   : public DeserializationNotificationHandler {
   void didDeserialize(ModuleDecl *M, PILFunction *fn) override {
      updateLinkage(fn);
   }

   void didDeserialize(ModuleDecl *M, PILGlobalVariable *var) override {
      updateLinkage(var);

      // For globals we currently do not support available_externally.
      // In the interpreter it would result in two instances for a single global:
      // one in the imported module and one in the main module.
      var->setDeclaration(true);
   }

   void didDeserialize(ModuleDecl *M, PILVTable *vtable) override {
      // TODO: should vtables get linkage?
      //updateLinkage(vtable);
   }

   void didDeserialize(ModuleDecl *M, PILWitnessTable *wt) override {
      updateLinkage(wt);
   }

   template <class T> void updateLinkage(T *decl) {
      switch (decl->getLinkage()) {
         case PILLinkage::Public:
            decl->setLinkage(PILLinkage::PublicExternal);
            return;
         case PILLinkage::PublicNonABI:
            // PublicNonABI functions receive SharedExternal linkage, so that
            // they have "link once" semantics when deserialized by multiple
            // translation units in the same Swift module.
            decl->setLinkage(PILLinkage::SharedExternal);
            return;
         case PILLinkage::Hidden:
            decl->setLinkage(PILLinkage::HiddenExternal);
            return;
         case PILLinkage::Shared:
            decl->setLinkage(PILLinkage::SharedExternal);
            return;
         case PILLinkage::Private:
            decl->setLinkage(PILLinkage::PrivateExternal);
            return;
         case PILLinkage::PublicExternal:
         case PILLinkage::HiddenExternal:
         case PILLinkage::SharedExternal:
         case PILLinkage::PrivateExternal:
            return;
      }
   }

   StringRef getName() const override {
      return "PILModule::SerializationCallback";
   }
};

PILModule::PILModule(ModuleDecl *polarphpModule, TypeConverter &TC,
                     PILOptions &Options, const DeclContext *associatedDC,
                     bool wholeModule)
   : ThePolarphpModule(polarphpModule),
     AssociatedDeclContext(associatedDC),
     Stage(PILStage::Raw), wholeModule(wholeModule), Options(Options),
     serialized(false), SerializePILAction(), Types(TC) {
   // We always add the base PILModule serialization callback.
   std::unique_ptr<DeserializationNotificationHandler> callback(
      new PILModule::SerializationCallback());
   deserializationNotificationHandlers.add(std::move(callback));
}

PILModule::~PILModule() {
   // Decrement ref count for each PILGlobalVariable with static initializers.
   for (PILGlobalVariable &v : silGlobals)
      v.dropAllReferences();

   // Drop everything functions in this module reference.
   //
   // This is necessary since the functions may reference each other.  We don't
   // need to worry about sil_witness_tables since witness tables reference each
   // other via protocol conformances and sil_vtables don't reference each other
   // at all.
   for (PILFunction &F : *this) {
      F.dropAllReferences();
      F.dropDynamicallyReplacedFunction();
   }
}

std::unique_ptr<PILModule>
PILModule::createEmptyModule(ModuleDecl *M, TypeConverter &TC, PILOptions &Options,
                             bool WholeModule) {
   return std::unique_ptr<PILModule>(
      new PILModule(M, TC, Options, M, WholeModule));
}

AstContext &PILModule::getAstContext() const {
   return ThePolarphpModule->getAstContext();
}

void *PILModule::allocate(unsigned Size, unsigned Align) const {
   if (getAstContext().LangOpts.UseMalloc)
      return aligned_alloc(Size, Align);

   return BPA.Allocate(Size, Align);
}

void *PILModule::allocateInst(unsigned Size, unsigned Align) const {
   return aligned_alloc(Size, Align);
}

void PILModule::deallocateInst(PILInstruction *I) {
   aligned_free(I);
}

PILWitnessTable *
PILModule::lookUpWitnessTable(InterfaceConformanceRef C,
                              bool deserializeLazily) {
   // If we have an abstract conformance passed in (a legal value), just return
   // nullptr.
   if (!C.isConcrete())
      return nullptr;

   return lookUpWitnessTable(C.getConcrete());
}

PILWitnessTable *
PILModule::lookUpWitnessTable(const InterfaceConformance *C,
                              bool deserializeLazily) {
   assert(C && "null conformance passed to lookUpWitnessTable");

   PILWitnessTable *wtable;

   auto rootC = C->getRootConformance();
   // Attempt to lookup the witness table from the table.
   auto found = WitnessTableMap.find(rootC);
   if (found == WitnessTableMap.end()) {
#ifndef NDEBUG
      // Make sure that all witness tables are in the witness table lookup
      // cache.
      //
      // This code should not be hit normally since we add witness tables to the
      // lookup cache when we create them. We don't just assert here since there
      // is the potential for a conformance without a witness table to be passed
      // to this function.
      for (PILWitnessTable &WT : witnessTables)
         assert(WT.getConformance() != rootC &&
                "Found witness table that is not"
                " in the witness table lookup cache.");
#endif

      // If we don't have a witness table and we're not going to try
      // deserializing it, do not create a declaration.
      if (!deserializeLazily)
         return nullptr;

      auto linkage = getLinkageForInterfaceConformance(rootC, NotForDefinition);
      wtable = PILWitnessTable::create(*this, linkage,
                                       const_cast<RootInterfaceConformance *>(rootC));
   } else {
      wtable = found->second;
      assert(wtable != nullptr && "Should never map a conformance to a null witness"
                                  " table.");

      // If we have a definition, return it.
      if (wtable->isDefinition())
         return wtable;
   }

   // If the module is at or past the Lowered stage, then we can't do any
   // further deserialization, since pre-IRGen PIL lowering changes the types
   // of definitions to make them incompatible with canonical serialized PIL.
   switch (getStage()) {
      case PILStage::Canonical:
      case PILStage::Raw:
         break;

      case PILStage::Lowered:
         return wtable;
   }

   // Otherwise try to deserialize it. If we succeed return the deserialized
   // function.
   //
   // *NOTE* In practice, wtable will be deserializedTable, but I do not want to rely
   // on that behavior for now.
   // TODO
//   if (deserializeLazily)
//      if (auto deserialized = getPILLoader()->lookupWitnessTable(wtable))
//         return deserialized;

   // If we fail, just return the declaration.
   return wtable;
}

PILDefaultWitnessTable *
PILModule::lookUpDefaultWitnessTable(const InterfaceDecl *Interface,
                                     bool deserializeLazily) {
   // Note: we only ever look up default witness tables in the translation unit
   // that is currently being compiled, since they PILGen generates them when it
   // visits the protocol declaration, and IRGen emits them when emitting the
   // protocol descriptor metadata for the protocol.

   auto found = DefaultWitnessTableMap.find(Interface);
   if (found == DefaultWitnessTableMap.end()) {
      if (deserializeLazily) {
         PILLinkage linkage =
            getPILLinkage(getDeclLinkage(Interface), ForDefinition);
         PILDefaultWitnessTable *wtable =
            PILDefaultWitnessTable::create(*this, linkage, Interface);
         /// TODO
//         wtable = getPILLoader()->lookupDefaultWitnessTable(wtable);
//         if (wtable)
//            DefaultWitnessTableMap[Interface] = wtable;
         return wtable;
      }

      return nullptr;
   }

   return found->second;
}

PILDefaultWitnessTable *
PILModule::createDefaultWitnessTableDeclaration(const InterfaceDecl *Interface,
                                                PILLinkage Linkage) {
   return PILDefaultWitnessTable::create(*this, Linkage, Interface);
}

void PILModule::deleteWitnessTable(PILWitnessTable *Wt) {
   auto Conf = Wt->getConformance();
   assert(lookUpWitnessTable(Conf, false) == Wt);
   WitnessTableMap.erase(Conf);
   witnessTables.erase(Wt);
}

const IntrinsicInfo &PILModule::getIntrinsicInfo(Identifier ID) {
   unsigned OldSize = IntrinsicIDCache.size();
   IntrinsicInfo &Info = IntrinsicIDCache[ID];

   // If the element was is in the cache, return it.
   if (OldSize == IntrinsicIDCache.size())
      return Info;

   // Otherwise, lookup the ID and Type and store them in the map.
   StringRef NameRef = getBuiltinBaseName(getAstContext(), ID.str(), Info.Types);
   Info.ID = getLLVMIntrinsicID(NameRef);

   return Info;
}

const BuiltinInfo &PILModule::getBuiltinInfo(Identifier ID) {
   unsigned OldSize = BuiltinIDCache.size();
   BuiltinInfo &Info = BuiltinIDCache[ID];

   // If the element was is in the cache, return it.
   if (OldSize == BuiltinIDCache.size())
      return Info;

   // Otherwise, lookup the ID and Type and store them in the map.
   // Find the matching ID.
   StringRef OperationName =
      getBuiltinBaseName(getAstContext(), ID.str(), Info.Types);

   // Several operation names have suffixes and don't match the name from
   // Builtins.def, so handle those first.
   if (OperationName.startswith("fence_"))
      Info.ID = BuiltinValueKind::Fence;
   else if (OperationName.startswith("cmpxchg_"))
      Info.ID = BuiltinValueKind::CmpXChg;
   else if (OperationName.startswith("atomicrmw_"))
      Info.ID = BuiltinValueKind::AtomicRMW;
   else if (OperationName.startswith("atomicload_"))
      Info.ID = BuiltinValueKind::AtomicLoad;
   else if (OperationName.startswith("atomicstore_"))
      Info.ID = BuiltinValueKind::AtomicStore;
   else if (OperationName.startswith("allocWithTailElems_"))
      Info.ID = BuiltinValueKind::AllocWithTailElems;
   else
      Info.ID = llvm::StringSwitch<BuiltinValueKind>(OperationName)
#define BUILTIN(id, name, attrs) .Case(name, BuiltinValueKind::id)
#include "polarphp/ast/BuiltinsDef.h"
         .Default(BuiltinValueKind::None);

   return Info;
}

PILFunction *PILModule::lookUpFunction(PILDeclRef fnRef) {
   auto name = fnRef.mangle();
   return lookUpFunction(name);
}

bool PILModule::loadFunction(PILFunction *F) {
   /// TODO
//   PILFunction *NewF =
//      getPILLoader()->lookupPILFunction(F, /*onlyUpdateLinkage*/ false);
//   if (!NewF)
   return false;

//   assert(F == NewF);
//   return true;
}

void PILModule::updateFunctionLinkage(PILFunction *F) {
//   getPILLoader()->lookupPILFunction(F, /*onlyUpdateLinkage*/ true);
}

bool PILModule::linkFunction(PILFunction *F, PILModule::LinkingMode Mode) {
   return PILLinkerVisitor(*this, Mode).processFunction(F);
}

PILFunction *PILModule::findFunction(StringRef Name, PILLinkage Linkage) {
   assert((Linkage == PILLinkage::Public ||
           Linkage == PILLinkage::PublicExternal) &&
          "Only a lookup of public functions is supported currently");

   PILFunction *F = nullptr;

   // First, check if there is a function with a required name in the
   // current module.
   PILFunction *CurF = lookUpFunction(Name);

   // Nothing to do if the current module has a required function
   // with a proper linkage already.
   if (CurF && CurF->getLinkage() == Linkage) {
      F = CurF;
   } else {
      assert((!CurF || CurF->getLinkage() != Linkage) &&
             "hasFunction should be only called for functions that are not "
             "contained in the PILModule yet or do not have a required linkage");
   }

   if (!F) {
      if (CurF) {
         // Perform this lookup only if a function with a given
         // name is present in the current module.
         // This is done to reduce the amount of IO from the
         // typephp module file.
//         if (!getPILLoader()->hasPILFunction(Name, Linkage))
//            return nullptr;
         /// TODO
         return nullptr;
         // The function in the current module will be changed.
         F = CurF;
      }

      // If function with a given name wasn't seen anywhere yet
      // or if it is known to exist, perform a lookup.
      if (!F) {
         // Try to load the function from other modules.
//         F = getPILLoader()->lookupPILFunction(Name, /*declarationOnly*/ true,
//                                               Linkage);
         // Bail if nothing was found and we are not sure if
         // this function exists elsewhere.
//         if (!F)
         // TODO
         return nullptr;
         assert(F && "PILFunction should be present in one of the modules");
         assert(F->getLinkage() == Linkage && "PILFunction has a wrong linkage");
      }
   }

   // If a function exists already and it is a non-optimizing
   // compilation, simply convert it into an external declaration,
   // so that a compiled version from the shared library is used.
   if (F->isDefinition() &&
       !F->getModule().getOptions().shouldOptimize()) {
      F->convertToDeclaration();
   }
   if (F->isExternalDeclaration())
      F->setSerialized(IsSerialized_t::IsNotSerialized);
   F->setLinkage(Linkage);
   return F;
}

bool PILModule::hasFunction(StringRef Name) {
   if (lookUpFunction(Name))
      return true;
   /// TODO
   return false;
//   return getPILLoader()->hasPILFunction(Name);
}

void PILModule::linkAllFromCurrentModule() {
/// TODO
//   getPILLoader()->getAllForModule(getTypePHPModule()->getName(),
//      /*PrimaryFile=*/nullptr);
}

void PILModule::invalidatePILLoaderCaches() {
/// TODO
//   getPILLoader()->invalidateCaches();
}

void PILModule::removeFromZombieList(StringRef Name) {
   if (auto *Zombie = ZombieFunctionTable.lookup(Name)) {
      ZombieFunctionTable.erase(Name);
      zombieFunctions.remove(Zombie);
   }
}

/// Erase a function from the module.
void PILModule::eraseFunction(PILFunction *F) {
   assert(!F->isZombie() && "zombie function is in list of alive functions");
   // The owner of the function's Name is the FunctionTable key. As we remove
   // the function from the table we have to store the name string elsewhere:
   // in zombieFunctionNames.
   StringRef copiedName = F->getName().copy(zombieFunctionNames);
   FunctionTable.erase(F->getName());
   F->Name = copiedName;

   // The function is dead, but we need it later (at IRGen) for debug info
   // or vtable stub generation. So we move it into the zombie list.
   getFunctionList().remove(F);
   zombieFunctions.push_back(F);
   ZombieFunctionTable[copiedName] = F;
   F->setZombie();

   // This opens dead-function-removal opportunities for called functions.
   // (References are not needed anymore.)
   F->dropAllReferences();
   F->dropDynamicallyReplacedFunction();
}

void PILModule::invalidateFunctionInPILCache(PILFunction *F) {
   /// TODo
//   getPILLoader()->invalidateFunction(F);
}

/// Erase a global PIL variable from the module.
void PILModule::eraseGlobalVariable(PILGlobalVariable *G) {
   GlobalVariableMap.erase(G->getName());
   getPILGlobalList().erase(G);
}

PILVTable *PILModule::lookUpVTable(const ClassDecl *C) {
   if (!C)
      return nullptr;

   // First try to look up R from the lookup table.
   auto R = VTableMap.find(C);
   if (R != VTableMap.end())
      return R->second;

   // If that fails, try to deserialize it. If that fails, return nullptr.
//   PILVTable *Vtbl = getPILLoader()->lookupVTable(C);
   /// TODO
   return nullptr;
//   if (!Vtbl)
//      return nullptr;

   // If we succeeded, map C -> VTbl in the table and return VTbl.
//   VTableMap[C] = Vtbl;
//   return Vtbl;
}

SerializedPILLoader *PILModule::getPILLoader() {
   // If the PILLoader is null, create it.
//   if (!PILLoader)
//      PILLoader = SerializedPILLoader::create(
//         getAstContext(), this, &deserializationNotificationHandlers);
//   // Return the SerializedPILLoader.
//   return PILLoader.get();
   /// TODO
   return nullptr;
}

/// Given a conformance \p C and a protocol requirement \p Requirement,
/// search the witness table for the conformance and return the witness thunk
/// for the requirement.
std::pair<PILFunction *, PILWitnessTable *>
PILModule::lookUpFunctionInWitnessTable(InterfaceConformanceRef C,
                                        PILDeclRef Requirement) {
   // Look up the witness table associated with our protocol conformance from the
   // PILModule.
   auto Ret = lookUpWitnessTable(C);

   // If no witness table was found, bail.
   if (!Ret) {
      LLVM_DEBUG(llvm::dbgs() << "        Failed speculative lookup of "
                                 "witness for: ";
                    C.dump(llvm::dbgs()); Requirement.dump());
      return std::make_pair(nullptr, nullptr);
   }

   // Okay, we found the correct witness table. Now look for the method.
   for (auto &Entry : Ret->getEntries()) {
      // Look at method entries only.
      if (Entry.getKind() != PILWitnessTable::WitnessKind::Method)
         continue;

      PILWitnessTable::MethodWitness MethodEntry = Entry.getMethodWitness();
      // Check if this is the member we were looking for.
      if (MethodEntry.Requirement != Requirement)
         continue;

      return std::make_pair(MethodEntry.Witness, Ret);
   }

   return std::make_pair(nullptr, nullptr);
}

/// Given a protocol \p Interface and a requirement \p Requirement,
/// search the protocol's default witness table and return the default
/// witness thunk for the requirement.
std::pair<PILFunction *, PILDefaultWitnessTable *>
PILModule::lookUpFunctionInDefaultWitnessTable(const InterfaceDecl *Interface,
                                               PILDeclRef Requirement,
                                               bool deserializeLazily) {
   // Look up the default witness table associated with our protocol from the
   // PILModule.
   auto Ret = lookUpDefaultWitnessTable(Interface, deserializeLazily);

   // If no default witness table was found, bail.
   //
   // FIXME: Could be an assert if we fix non-single-frontend mode to link
   // together serialized PIL emitted by each translation unit.
   if (!Ret) {
      LLVM_DEBUG(llvm::dbgs() << "        Failed speculative lookup of default "
                                 "witness for " << Interface->getName() << " ";
                    Requirement.dump());
      return std::make_pair(nullptr, nullptr);
   }

   // Okay, we found the correct default witness table. Now look for the method.
   for (auto &Entry : Ret->getEntries()) {
      // Ignore dummy entries emitted for non-method requirements, as well as
      // requirements without default implementations.
      if (!Entry.isValid() || Entry.getKind() != PILWitnessTable::Method)
         continue;

      // Check if this is the member we were looking for.
      if (Entry.getMethodWitness().Requirement != Requirement)
         continue;

      return std::make_pair(Entry.getMethodWitness().Witness, Ret);
   }

   // This requirement doesn't have a default implementation.
   return std::make_pair(nullptr, nullptr);
}

PILFunction *
PILModule::
lookUpFunctionInVTable(ClassDecl *Class, PILDeclRef Member) {
   // Try to lookup a VTable for Class from the module...
   auto *Vtbl = lookUpVTable(Class);

   // Bail, if the lookup of VTable fails.
   if (!Vtbl) {
      return nullptr;
   }

   // Ok, we have a VTable. Try to lookup the PILFunction implementation from
   // the VTable.
   if (auto E = Vtbl->getEntry(*this, Member))
      return E->Implementation;

   return nullptr;
}

void PILModule::registerDeserializationNotificationHandler(
   std::unique_ptr<DeserializationNotificationHandler> &&handler) {
   deserializationNotificationHandlers.add(std::move(handler));
}

void PILModule::registerDeleteNotificationHandler(
   DeleteNotificationHandler *handler) {
   // Ask the handler (that can be an analysis, a pass, or some other data
   // structure) if it wants to receive delete notifications.
   if (handler->needsNotifications()) {
      NotificationHandlers.insert(handler);
   }
}

void PILModule::
removeDeleteNotificationHandler(DeleteNotificationHandler* Handler) {
   NotificationHandlers.remove(Handler);
}

void PILModule::notifyDeleteHandlers(PILNode *node) {
   for (auto *Handler : NotificationHandlers) {
      Handler->handleDeleteNotification(node);
   }
}

// TODO: We should have an "isNoReturn" bit on Swift's BuiltinInfo, but for
// now, let's recognize noreturn intrinsics and builtins specially here.
bool PILModule::isNoReturnBuiltinOrIntrinsic(Identifier Name) {
   const auto &IntrinsicInfo = getIntrinsicInfo(Name);
   if (IntrinsicInfo.ID != llvm::Intrinsic::not_intrinsic) {
      return IntrinsicInfo.hasAttribute(llvm::Attribute::NoReturn);
   }
   const auto &BuiltinInfo = getBuiltinInfo(Name);
   switch (BuiltinInfo.ID) {
      default:
         return false;
      case BuiltinValueKind::Unreachable:
      case BuiltinValueKind::CondUnreachable:
      case BuiltinValueKind::UnexpectedError:
      case BuiltinValueKind::ErrorInMain:
         return true;
   }
}

bool PILModule::
shouldSerializeEntitiesAssociatedWithDeclContext(const DeclContext *DC) const {
   // Serialize entities associated with this module's associated context.
   if (DC->isChildContextOf(getAssociatedContext())) {
      return true;
   }

   // Serialize entities associated with clang modules, since other entities
   // may depend on them, and someone who deserializes those entities may not
   // have their own copy.
   if (isa<ClangModuleUnit>(DC->getModuleScopeContext())) {
      return true;
   }

   return false;
}

/// Returns true if it is the optimized OnoneSupport module.
bool PILModule::isOptimizedOnoneSupportModule() const {
   return getOptions().shouldOptimize() &&
          getTypePHPModule()->isOnoneSupportModule();
}

void PILModule::setSerializePILAction(PILModule::ActionCallback Action) {
   assert(!SerializePILAction && "Serialization action can be set only once");
   SerializePILAction = Action;
}

PILModule::ActionCallback PILModule::getSerializePILAction() const {
   return SerializePILAction;
}

void PILModule::serialize() {
   assert(SerializePILAction && "Serialization action should be set");
   assert(!isSerialized() && "The module was serialized already");
   SerializePILAction();
   setSerialized();
}

void PILModule::setOptRecordStream(
   std::unique_ptr<llvm::yaml::Output> &&Stream,
   std::unique_ptr<llvm::raw_ostream> &&RawStream) {
   OptRecordStream = std::move(Stream);
   OptRecordRawStream = std::move(RawStream);
}

bool PILModule::isStdlibModule() const {
   return ThePolarphpModule->isStdlibModule();
}

PILProperty *PILProperty::create(PILModule &M,
                                 bool Serialized,
                                 AbstractStorageDecl *Decl,
                                 Optional<KeyPathPatternComponent> Component) {
   auto prop = new (M) PILProperty(Serialized, Decl, Component);
   M.properties.push_back(prop);
   return prop;
}

// Definition from PILLinkage.h.
PILLinkage polar::getDeclPILLinkage(const ValueDecl *decl) {
   AccessLevel access = decl->getEffectiveAccess();
   PILLinkage linkage;
   switch (access) {
      case AccessLevel::Private:
      case AccessLevel::FilePrivate:
         linkage = PILLinkage::Private;
         break;
      case AccessLevel::Internal:
         linkage = PILLinkage::Hidden;
         break;
      case AccessLevel::Public:
      case AccessLevel::Open:
         linkage = PILLinkage::Public;
         break;
   }
   return linkage;
}
